package vpcserver

import (
	"context"
	"encoding/json"
	"net"
	"sync"
	"time"
)

// Proxy 一个资产对应一个Proxy
type Proxy struct {
	ProxyId              string
	AssetAddr            string             //目标资产地址IP:PORT
	RemoteConnWrapper    *RemoteConnWrapper //远程隧道连接代理
	ClientConnMap        sync.Map           //客户端连接池
	Listener             net.Listener       //本地监听地址
	LastVisitTime        time.Time          //该监听地址最后一次业务访问时间
	TcpBufSize           int                //TCP转发的缓冲区大小 默认128K
	ProxyMaxIdealTimeOut int                //Proxy Tcp业务连接最大空闲时间，默认24小时
	AssetMaxIdealTimeOut int                //Proxy控制连接最大空闲时间，默认3分钟
}

// ClientConnWrapper 客户端连接包装结构体
type ClientConnWrapper struct {
	ClientConn    net.Conn //客户端资产会话链接
	ClientAddr    string
	RemoteAddr    string
	LastVisitTime time.Time //最后一次转发报文的时间
}

// RemoteConnWrapper 远程隧道包装结构体
type RemoteConnWrapper struct {
	ProxyId           string
	ioChan            chan []byte //隧道消息通道
	RemoteConn        net.Conn    //客户端资产会话链接
	Lock              sync.Mutex  //锁
	lastHeartBeatTime time.Time
}

func (proxy *Proxy) run() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()
	go proxy.clearUnUseAssetConn(ctx)
	for {
		conn, err := proxy.Listener.Accept()
		if err != nil {
			return
		}
		//更新本资产的最后一个访问时间
		proxy.LastVisitTime = time.Now()
		//conn.RemoteAddr()为引擎侧客户端地址
		wrapperConn := &ClientConnWrapper{ClientConn: conn, ClientAddr: conn.RemoteAddr().String(), RemoteAddr: proxy.RemoteConnWrapper.RemoteConn.LocalAddr().String(), LastVisitTime: time.Now()}
		proxy.ClientConnMap.Store(conn.RemoteAddr().String(), wrapperConn)
		go proxy.handler(conn)
	}
}
func (proxy *Proxy) handler(conn net.Conn) {
	defer func() {
		if e := recover(); e != nil {

		}
		proxy.ClientConnMap.Delete(conn.RemoteAddr().String())
		_ = conn.Close()
	}()
	buf := make([]byte, 1024*proxy.TcpBufSize)
	dto := &AgentDto{Action: Data, ProxyId: proxy.ProxyId, ClientAddr: conn.RemoteAddr().String(), AssetAddr: proxy.AssetAddr}
	head, _ := json.Marshal(dto)
	//先发一波请求，让目标资产端先建立连接
	// 2024-08-27 解决VNC协议问题 加入
	proxy.RemoteConnWrapper.ioChan <- PayLoadEncode(VpcAgentResponse, head, []byte{})
	for {
		n, err := conn.Read(buf)
		if err != nil {
			return
		}
		if n > 0 {
			proxy.RemoteConnWrapper.ioChan <- PayLoadEncode(VpcAgentResponse, head, buf[:n])
			//更新本次业务资产连接的最新业务访问时间
			conn, ok := proxy.ClientConnMap.Load(conn.RemoteAddr().String())
			if ok {
				conn.(*ClientConnWrapper).LastVisitTime = time.Now()
			}
		}
	}
}
func (proxy *Proxy) clearUnUseAssetConn(ctx context.Context) {
	ticker := time.NewTicker(time.Second * time.Duration(5))
	defer ticker.Stop()
	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
			//超过3分钟没人访问的业务入口，进行关闭
			proxy.cleanIdealConn(time.Duration(proxy.AssetMaxIdealTimeOut) * time.Minute)
		}
	}
}
func (proxy *Proxy) stop() {
	proxy.cleanIdealConn(time.Duration(1) * time.Millisecond)
	_ = proxy.Listener.Close()
}
func (proxy *Proxy) cleanIdealConn(timeout time.Duration) {
	proxy.ClientConnMap.Range(func(key, value any) bool {
		wrapperConn, ok := value.(*ClientConnWrapper)
		if ok {
			if time.Now().Sub(wrapperConn.LastVisitTime) >= timeout {
				_ = wrapperConn.ClientConn.Close()
				proxy.ClientConnMap.Delete(key.(string))
			}
		}
		return true
	})
}
